`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

/**
	@file MemoryMappedNorFlashController.v
	@author Andrew D. Zonenberg
	@brief Memory mapping wrapper around the NOR flash
 */
module MemoryMappedNorFlashController(
	clk,
	addr, wr, rd, din, wmask, dout, doutok,
	spi_sck, spi_cs_n, spi_data
    );
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// IO declarations
	input wire clk;
	
	//Memory bus
	input wire[31:0] addr;
	input wire wr;
	input wire rd;
	input wire[31:0] din;
	input wire[3:0] wmask;
	output reg[31:0] dout = 0;
	output reg doutok = 0;
	
	//SPI bus
	output wire spi_sck;
	output wire spi_cs_n;
	inout wire[3:0] spi_data;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// The flash controller
	wire spi_busy;
	reg cmd_en = 0;
	reg[3:0] cmd_din = 0;
	reg[23:0] cmd_addr = 0;
	reg[7:0] cmd_burstlen = 0;
	reg read_en = 0;
	wire[7:0] read_data;
	wire read_overflow;
	wire read_underflow;
	wire read_full;
	wire read_empty;
	SpiNorFlashController controller (
		.clk(clk), 
		.busy(spi_busy), 
		.spi_sck(spi_sck), 
		.spi_cs_n(spi_cs_n), 
		.spi_data(spi_data), 
		.cmd_en(cmd_en), 
		.cmd_din(cmd_din), 
		.cmd_addr(cmd_addr), 
		.cmd_burstlen(cmd_burstlen), 
		.read_en(read_en), 
		.read_data(read_data), 
		.read_overflow(read_overflow), 
		.read_underflow(read_underflow), 
		.read_full(read_full), 
		.read_empty(read_empty)
		);
		
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Memory mapping state machine
	// For now there is no caching or prefetching. Super slow but adequate for a boot loader etc.
	
	//Convert the peripheral address to the flash physical address
	//Flash controller is mapped starting at bfb0 0000.
	//Memory array doesn't start till bfb2 0000.
	localparam MEM_START = 32'h20000;
	wire sfr_range;
	assign sfr_range = (addr[31:0] < MEM_START);
	wire[19:0] memory_addr;
	assign memory_addr = addr[19:0] - MEM_START;
	
	reg read_active = 0;
	
	reg[9:0] tcount = 10'h1;
	reg ready = 0;

	reg read_en_buf = 0;
	reg[2:0] bytecount = 0;
	
	reg[31:0] last_read_addr = 0;
	reg[31:0] last_read_data = 0;
	always @(posedge clk) begin
	
		cmd_en <= 0;
		cmd_din <= 0;
		cmd_addr <= 0;
		cmd_burstlen <= 0;
		read_en <= 0;
		read_en_buf <= read_en;
		doutok <= 0;
	
		//Wait >= 10us for flash to power up before doing anything
		//At 80 MHz this is 800 clocks. We'll use a 10-bit counter (1024 clocks) just to keep things simple.
		if(!ready) begin
			tcount <= tcount + 10'd1;
			if(tcount == 0)
				ready <= 1;
		end
		
		//TODO: read JEDEC ID and serial number from flash during boot process
		//so we can just cache them locally
		
		//Flash is booted up, ready to process commands
		else begin
		
			if(wr) begin
				$display("[MemoryMappedNorFlashController] Warning - attempting to write to flash (not implemented)");
			end
			
			//Read logic
			if(rd) begin
				
				//Separate stuff for the SFR range!
				//Not implemented yet
				if(sfr_range) begin
					$display("[MemoryMappedNorFlashController] Warning - attempting to read from SFR range (not implemented)");
				end
				
				else begin
					
					//If this is a fresh read, request a 4-byte read from the flash
					//Pad address up to 24 bits with zeros
					if(!read_active) begin
					
						//If this is a repeat of the last read, return the cached data
						if(last_read_addr == addr) begin
							doutok <= 1;
							dout <= last_read_data;
						end
						
						else begin
							read_active <= 1;
							cmd_addr <= {4'b0, memory_addr};
							cmd_burstlen <= 4;
							cmd_din <= 4'b0011;			//CMD_READ_DATA
							cmd_en <= 1;
							bytecount <= 0;
							last_read_addr <= addr;
						end
						
					end
					
					//If a read is in progress, check if it completed.
					//If so, output the response. If not, wait.
					else begin
					
						if(bytecount == 4) begin
							
							//Sanity check that we're asking for the same address!
							//If not, throw away the data and restart
							if(last_read_addr == addr) begin
								doutok <= 1;
							end
							
							read_active <= 0;
						end
					end
					
				end
				
			end	//if(rd)
			
			//Pop the read FIFO (but not twice in a row)
			if(!read_empty && !read_en && !read_en_buf) begin
				read_en <= 1;
			end
			if(read_en_buf) begin
				dout <= {read_data, dout[31:8]};
				last_read_data <= {read_data, dout[31:8]};
				bytecount <= bytecount + 3'd1;
			end
			
		end	//if(ready)
		
	end
		
endmodule
